home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / audio / sonic / sonic.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  22.9 KB  |  632 lines

  1. /*
  2.  * Copyright (c) 1992, 1993, 1994 Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * Permission to use, copy, modify, distribute, and sell this software and
  6.  * its documentation for any purpose is hereby granted without fee, provided
  7.  * that (i) the above copyright notices and this permission notice appear in
  8.  * all copies of the software and related documentation, and (ii) the name of
  9.  * Silicon Graphics may not be used in any advertising or
  10.  * publicity relating to the software without the specific, prior written
  11.  * permission of Silicon Graphics.
  12.  * 
  13.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
  14.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  15.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  16.  *
  17.  * IN NO EVENT SHALL SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, 
  18.  * INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY 
  19.  * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
  20.  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  21.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  22.  * OF THIS SOFTWARE.
  23.  */
  24.  
  25. /*
  26.  * sonic.c - simple real time audio spatialization example with graphics 
  27.  *
  28.  * This code originally appeared in the March/April 1993 Issue of the 
  29.  * Silicon Graphics support magazine "Pipeline", Volume 4, Number 2
  30.  * accompanying the article titled "Adding Audio to an Existing Graphics
  31.  * Application" as a coding example.
  32.  *
  33.  * A few extra comments have been added from the code in the article.
  34.  * The functioning code is exactly the same as that of the article.
  35.  */
  36.  
  37. /* --------------------------------------------------------------------- */
  38.  
  39. /* sonic.c - 11/92 Dan Fink */
  40. /* For use with the Digital Media Development Option */
  41. /* On Indigo family hardware platforms running IRIX 4.0.5F or better */
  42. /* Compile with -lgl -lsphere -laudio -laudiofile -lm */
  43.  
  44. #include <limits.h>
  45. #include <sys/types.h>
  46. #include <sys/prctl.h>
  47. #include <sys/schedctl.h>
  48. #include <sys/lock.h>
  49. #include <stdio.h>
  50. #include <malloc.h>
  51. #include <gl/gl.h>
  52. #include <gl/device.h>
  53. #include <gl/sphere.h>
  54. #include <math.h>
  55. #include <audio.h>
  56. #include <audiofile.h>
  57.  
  58. /* AUDIO DEFINES */
  59. #define BUFFERSIZE 4000            /* Size of output sample buffer. */
  60. #define PI 3.141592653             /* Our old friend from trig.     */
  61. /* the following originated from the dmedia_tools.data.prosonus subsystem:
  62. #define BALLFILENAME "/usr/lib/sounds/prosonus/instr/metal_triad_power.E1.aiff"
  63. #define WALLFILENAME "/usr/lib/sounds/prosonus/musictags/slinky_slap.aiff"
  64. */
  65. #define BALLFILENAME "metal_triad_power.E1.aiff"
  66. #define WALLFILENAME "slinky_slap.aiff"
  67.  
  68. /* OTHER DEFINES */
  69. #define SPHERERADIUS 1.0
  70. #define BOXSIZE 20.0
  71. #define NO_HIT 0
  72. #define JUST_HIT 1
  73. #define BEEN_HIT 2
  74.  
  75. /* GRAPHICS PROTOTYPES */
  76. void sceneinit(long, long);
  77. Object makebox();
  78. void drawscene();
  79. void movesphere();
  80.  
  81. /* AUDIO PROTOTYPES */
  82. void audioinit();
  83. long init_sound(float **, char*);
  84. void audioloop(void *);
  85. void process_oneshot_audio( float *, float, float, long);
  86. void process_continuous_audio( float *, float, float, long);
  87. void audioexit(int);
  88.  
  89.  
  90. /* AUDIO RELATED GLOBALS */
  91. float *ballbuf, *wallbuf;       /* Sample buffers for ball and wall sounds */
  92. long  ballframes,wallframes;    /* Number of samples in each of these buffers */
  93. ALconfig audioconfig;           /* Audio Library port configuration */
  94. ALport audioport;               /* Audio Library audio port */
  95. AFfilesetup afsetup;            /* Audio File Library file setup */
  96. AFfilehandle affh;              /* Audio File Library file handle */
  97.  
  98. /* OTHER GLOBALS */
  99. Object sphere,box;              /* GL object identifiers for ball and box */
  100. float rx, ry;                   /* Angles of rotation for observer */
  101. float sphx,sphy,sphz;           /* Coordinates of sphere */
  102. float movx,movy,movz;           /* Vector for sphere motion */
  103. float hitx, hity, hitz;         /* Coordinates of ball hitting wall */
  104. int hit;                        /* Status of ball hitting wall */
  105. int done;                       /* Indicates completion of graphics process */
  106. int audiodone;                  /* Indicates completion of audio process */
  107.  
  108.  
  109. /* GRAPHICS ROUTINES */
  110. void sceneinit(long xsize, long ysize)    /* Iniitialize scene and GL set up */
  111. {
  112.     static Matrix Identity = {  1, 0, 0, 0,  0, 1, 0, 0,  
  113.                                 0, 0, 1, 0,  0, 0, 0, 1 };
  114.  
  115.     static float mat[] = {  AMBIENT, .1, .1, .1,
  116.                             DIFFUSE, 0, .369, .165,
  117.                             SPECULAR, .5, .5, .5,
  118.                             SHININESS, 10,
  119.                             LMNULL };   /* Material of sphere */
  120.  
  121.     static float lm[] = {   AMBIENT, .1, .1, .1,
  122.                             LOCALVIEWER, 1,
  123.                             LMNULL };   /* Lighting model for sphere */
  124.  
  125.     static float lt[] = {   LCOLOR, 1, 1, 1,
  126.                             POSITION, 0, 0, 1, 0,
  127.                             LMNULL };   /* Light for sphere */
  128.  
  129.     /* Set up projection matrix and model-view stack */
  130.     mmode(MVIEWING);
  131.     loadmatrix(Identity);
  132.     perspective(600, xsize/(float)ysize, .25, BOXSIZE*1.718);
  133.  
  134.     /* Initialize GL object for sphere using sphere library */
  135.     sphere = genobj();
  136.     sphmode (SPH_TESS,   SPH_OCT);
  137.         sphmode (SPH_DEPTH,  4);
  138.         sphmode (SPH_PRIM,   SPH_MESH);
  139.         sphmode (SPH_HEMI,   FALSE);
  140.         sphmode (SPH_ORIENT, FALSE);
  141.     sphobj(sphere);
  142.  
  143.     box = makebox();         /* Initialize GL object for walls (box) */
  144.  
  145.     /* Initialize lights */
  146.     lmdef(DEFMATERIAL, 1, 0, mat);
  147.     lmdef(DEFLIGHT, 1, 0, lt);
  148.     lmdef(DEFLMODEL, 1, 0, lm);
  149.     lmbind(MATERIAL, 1);
  150.     lmbind(LMODEL, 1);
  151.     lmbind(LIGHT0, 1);
  152. }
  153.  
  154.  
  155. Object makebox()            /* Returns a GL object that defines walls (box) */
  156. {
  157.     Object obj=genobj();                /* GL object to contain the box  */
  158.     static float v[8][3] = {            /* Vertices for box */
  159.             { -1.0, -1.0, -1.0 }, {  1.0, -1.0, -1.0 },
  160.             {  1.0,  1.0, -1.0 }, { -1.0,  1.0, -1.0 },
  161.             { -1.0, -1.0,  1.0 }, {  1.0, -1.0,  1.0 },
  162.             {  1.0,  1.0,  1.0 }, { -1.0,  1.0,  1.0 } };
  163.  
  164.     makeobj(obj);
  165.     bgntmesh(); /* tmesh for cube */
  166.         cpack(0x000060); v3f(v[1]);
  167.         cpack(0x000000); v3f(v[0]);
  168.         cpack(0x006060); v3f(v[2]);
  169.         cpack(0x006000); v3f(v[3]);
  170.         cpack(0x606060); v3f(v[6]);
  171.  
  172.         swaptmesh();
  173.         cpack(0x606000); v3f(v[7]);
  174.         cpack(0x000000); v3f(v[0]);
  175.         cpack(0x600000); v3f(v[4]);
  176.  
  177.         swaptmesh();
  178.         cpack(0x600060); v3f(v[5]);
  179.         cpack(0x000060); v3f(v[1]);
  180.  
  181.         swaptmesh();
  182.         cpack(0x006060); v3f(v[2]);
  183.         cpack(0x606060); v3f(v[6]);
  184.         cpack(0x606000); v3f(v[7]);
  185.  
  186.         swaptmesh();
  187.         cpack(0x600000); v3f(v[4]);
  188.         cpack(0x600060); v3f(v[5]);
  189.     endtmesh();
  190.  
  191.     pushmatrix();             /* Lines for cube outline */
  192.     scale(0.99,0.99,0.99);    /* Shrink lines just a tad, so they're visible */
  193.     cpack(0xa0a0a0);
  194.     bgnline(); 
  195.         v3f(v[0]); v3f(v[1]);
  196.         v3f(v[2]); v3f(v[3]);
  197.         v3f(v[0]); v3f(v[4]);
  198.         v3f(v[5]); v3f(v[6]);
  199.         v3f(v[7]); v3f(v[4]);
  200.     endline();
  201.  
  202.     bgnline();
  203.         v3f(v[1]); v3f(v[5]);
  204.     endline();
  205.  
  206.     bgnline();
  207.         v3f(v[2]); v3f(v[6]);
  208.     endline();
  209.  
  210.     bgnline();
  211.         v3f(v[3]); v3f(v[7]);
  212.     endline();
  213.     popmatrix();
  214.  
  215.     closeobj();
  216.     return(obj);
  217. }
  218.  
  219.  
  220. void drawscene()        /* Draw current state of ball and walls */
  221. {
  222.     pushmatrix();
  223.     rot(ry, 'y');       /* Viewpoint orientation */
  224.     rot(rx, 'x');       /* (We'll see these again in the audio routines) */
  225.  
  226.     backface(FALSE);    /* Draw cube - don't need z buffer for this geometry */
  227.     pushmatrix();
  228.     scale(BOXSIZE,BOXSIZE,BOXSIZE);
  229.     callobj(box);
  230.     popmatrix();
  231.  
  232.     backface(TRUE);     /* Draw sphere - don't need z buffer for this geometry*/
  233.     pushmatrix();
  234.     translate(sphx,sphy,sphz);
  235.     scale(SPHERERADIUS,SPHERERADIUS,SPHERERADIUS);
  236.     callobj(sphere);
  237.     popmatrix();
  238.  
  239.     popmatrix();
  240.     swapbuffers();
  241. }
  242.  
  243.  
  244. void movesphere()        /* Add motion to sphere.  Check for wall collisions */
  245. {
  246.     sphx += movx; sphy += movy; sphz += movz;    
  247.                         /* Move sphere in current direction */
  248.     
  249.     if (sphx > (BOXSIZE - SPHERERADIUS)) {    /* Check for x bounce */
  250.         movx = -movx;
  251.         sphx = 2*(BOXSIZE - SPHERERADIUS) - sphx; 
  252.         hit = JUST_HIT;
  253.     } 
  254.     else if (sphx < (SPHERERADIUS - BOXSIZE)) {
  255.         movx = -movx;
  256.         sphx = 2*(SPHERERADIUS - BOXSIZE) - sphx; 
  257.         hit = JUST_HIT;
  258.     }
  259.  
  260.     if (sphy > (BOXSIZE - SPHERERADIUS)) {    /* Check for y bounce */
  261.         movy = -movy;
  262.         sphy = 2*(BOXSIZE - SPHERERADIUS) - sphy; 
  263.         hit = JUST_HIT;
  264.     } 
  265.     else if (sphy < (SPHERERADIUS - BOXSIZE)) {
  266.         movy = -movy;
  267.         sphy = 2*(SPHERERADIUS - BOXSIZE) - sphy; 
  268.         hit = JUST_HIT;
  269.     }
  270.  
  271.     if (sphz > (BOXSIZE - SPHERERADIUS)) {    /* Check for z bounce */
  272.         movz = -movz;
  273.         sphz = 2*(BOXSIZE - SPHERERADIUS) - sphz; 
  274.         hit = JUST_HIT;
  275.     } 
  276.     else if (sphz < (SPHERERADIUS - BOXSIZE)) {
  277.         movz = -movz;
  278.         sphz = 2*(SPHERERADIUS - BOXSIZE) - sphz; 
  279.         hit = JUST_HIT;
  280.     }
  281.  
  282.     if (hit == JUST_HIT) {                     /* Record where bounced */
  283.             hitx = sphx; hity=sphy; hitz=sphz;
  284.     }
  285. }
  286.  
  287.  
  288. /* AUDIO ROUTINES */
  289. void audioinit() /* Configure the audio port */
  290. {
  291.     ballframes = init_sound( &ballbuf, BALLFILENAME); /* Get the ball waveform*/
  292.     wallframes = init_sound( &wallbuf, WALLFILENAME); /* Get the wall waveform*/
  293.  
  294.     audioconfig = ALnewconfig();             /* Get a new audio configuration */    
  295.  
  296.     ALsetsampfmt( audioconfig, AL_SAMPFMT_FLOAT); /* Sample format uses floats*/
  297.     ALsetfloatmax(audioconfig, 1.0);      /* Floats will vary from -1.0 to 1.0*/
  298.  
  299.     ALsetqueuesize(audioconfig,2*BUFFERSIZE); 
  300.             /* Set the sample queue size to be twice that of our sound buffer */
  301.  
  302.     ALsetchannels(audioconfig,AL_STEREO);             /* Use stereo */
  303.  
  304.     audioport = ALopenport("sine","w",audioconfig); /* Open audio for writing */
  305.     if (audioport == NULL) {
  306.         printf("couldn't open audio port.\n");
  307.         audioexit(-1);
  308.     }
  309. }
  310.  
  311.  
  312. long init_sound(float **floatbuf, char *filename)         /* Read audio file */
  313. {
  314. /*  This routine reads the specified audio file, converts the samples
  315.     to floating point, and return the length of the sample buffer.
  316.     For simplicity sake, we're looking to read in 16 bit sounds in 
  317.     AF_SAMPFMT_TWOSCOMP format.  Since our sound is eminating from a point
  318.     source, we can only use mono information.  For stereo sound files,
  319.     only the left buffer is used.  */
  320.  
  321.     long sampfmt;       /* Sample format of the audio file */
  322.     long numbits;       /* Number of bits wide the samples are in audio file */
  323.     long numchannels;   /* Number of channels (1=mono,2=stereo) in audio file */
  324.     long numframes;     /* Number of sample frames in audio file */
  325.     short *tempbuf;     /* Temporary buffer to store integer samples */
  326.     int i;              /* Counter */
  327.  
  328.     if((afsetup = AFnewfilesetup()) == NULL) { /* Create new audio file setup */
  329.         printf("Null file setup.\n");
  330.         audioexit(-2);
  331.     }
  332.  
  333.     if((affh = AFopenfile(filename,"r",afsetup)) == NULL) {  /*Open audio file*/
  334.         printf("Couldn't open audio file %s.\n",filename);
  335.         audioexit(-3);
  336.     }
  337.  
  338.     /* Determine validity of sample format */
  339.     AFgetsampfmt(affh, AF_DEFAULT_TRACK, &sampfmt,&numbits); 
  340.     if(sampfmt != AF_SAMPFMT_TWOSCOMP) {
  341.         printf("Strange audio file format in %s.\n",filename);
  342.         audioexit(-4);
  343.     }
  344.  
  345.     if (numbits != 16) {   /* Check sample width */
  346.         printf("Sample format isn't 16 bits in file %s.\n", filename);
  347.         audioexit(-5);
  348.     }
  349.  
  350.     numchannels = AFgetchannels(affh, AF_DEFAULT_TRACK);
  351.     numframes = AFgetframecnt(affh, AF_DEFAULT_TRACK);    /* Find buffer size */
  352.     tempbuf = (short *)malloc(numframes*numchannels*sizeof(short));
  353.  
  354.     /* Read 16 bit samples into temp buffer before float conversion */
  355.     if(AFreadframes(affh,AF_DEFAULT_TRACK,tempbuf,numframes) != numframes) {
  356.         printf("Error reading audio file %s.\n",filename);
  357.         audioexit(-6);
  358.     }
  359.     
  360.     *floatbuf = (float *)malloc(numframes*sizeof(float)); 
  361.                 /* Create a buffer of floats - floats are much nicer than */
  362.                 /* integers for the amplitude calculations later on.      */
  363.  
  364.     for(i=0; i<numframes; i++)
  365.         (*floatbuf)[i] = (float)tempbuf[i*numchannels]/32767.0;
  366.  
  367.                     /* Convert two's complement data into floating point data.*/
  368.                     /* Scale all samples to fit between -1.0 and 1.0. If the  */
  369.                     /* file is stereo, then only use data from the left       */
  370.                     /* channel. We are using a point source of sound here.    */
  371.  
  372.     free(tempbuf);        /* Free structures and close the audio file */
  373.     AFclosefile(affh);
  374.     affh = AF_NULL_FILEHANDLE;
  375.     AFfreefilesetup(afsetup);
  376.  
  377.     return(numframes);    /* Return number of frames read in */
  378. }
  379.  
  380.  
  381. void audioloop(void *dummy)        /* Audio process main loop */
  382. {
  383.     float distance,amplitude,balance;   /* What we need to find out */
  384.     float soundbuf[BUFFERSIZE];         /* Sample buffer for computations */
  385.     float relx,rely,relz;      /* Coordinates of ball relative to orientation */
  386.     float radx,rady;           /* Angle describing orientation in radians */
  387.  
  388.     if(schedctl(NDPRI,0,NDPHIMAX) == -1)
  389.       printf("Couldn't run at high, non-degrading priority.\n");
  390.                             /* Make this process run at a high, non-        */
  391.                             /* degrading priority. This help significantly  */
  392.                             /* "smooth out" audio clicking caused by losing */
  393.                             /* the CPU to other processes.  Works best when */
  394.                             /* the effective user id is the root user.      */
  395.     if(plock(PROCLOCK)!=0)
  396.       printf("Couldn't lock process into memory.\n");
  397.                                /* Lock this process into memory - make it  */
  398.                                /* immune to page swapping. Again, this is  */
  399.                                /* another aid in preventing clicking which */
  400.                                /* works when the effective user id is the  */
  401.                                /* root user.                               */
  402.  
  403.  
  404.     audioinit();               /* Initialize audio */
  405.     while(!done) {             /* Keep going until gfx process says otherwise */
  406.  
  407.         /* Put sphere's coordinates through viewing transformation */
  408.         /* Rot x (rx) * Rot y (rx) just like the graphics are doing */
  409.         /* If translate() or scale() calls were used, they would need to */
  410.         /* be represented here as well. Remember that the graphics pipe */
  411.         /* pre-multiplies for matrix operations! */
  412.         
  413.         radx = PI*rx/180.0;
  414.         rady = PI*ry/180.0;
  415.  
  416.         relx =  sphx*cos(rady) + sphy*sin(radx)*sin(rady) + 
  417.                 sphz*cos(radx)*sin(rady);
  418.         rely =  sphy*cos(radx) - sphz*sin(radx);
  419.         relz = -sphx*sin(rady) + sphy*sin(radx)*cos(rady) + 
  420.                 sphz*cos(radx)*cos(rady);
  421.  
  422.         distance = fsqrt( relx*relx + rely*rely + relz*relz );
  423.  
  424.         amplitude = 1.0 / (distance*distance + 1.0);  
  425.             /* Use inverse square proportion for amplitude */
  426.  
  427.         balance = acos( relx/distance) / PI;
  428.             /* Compute the audio orientation of the sphere by taking the     */
  429.             /* angle of the projected vector from the observer to the object */
  430.             /* the object onto the xy plane of the observer.  Left-right     */
  431.             /* balance is easiest to achieve with a 2 speaker system such as */
  432.             /* headphones, so we'll need to use the distance in the          */
  433.             /* x direction (relative to the observer's orientation) to find  */
  434.             /* left-right balance.  With only 2 speakers top-bottom balance  */
  435.             /* is very difficult to simulate, so we'll ignore it here.       */
  436.  
  437.         process_continuous_audio(soundbuf,amplitude,balance,BUFFERSIZE);
  438.  
  439.         if(hit != NO_HIT) {  /* Don't bother with walls if we don't need to */
  440.  
  441.             /* Pass the coordinates of the hit through the same */
  442.             /* transformations as above.                        */
  443.  
  444.             relx =  hitx*cos(rady) + hity*sin(radx)*sin(rady) + 
  445.                     hitz*cos(radx)*sin(rady);
  446.             rely =  hity*cos(radx) - hitz*sin(radx);
  447.             relz = -hitx*sin(rady) + hity*sin(radx)*cos(rady) + 
  448.                     hitz*cos(radx)*cos(rady);
  449.  
  450.             distance = fsqrt( relx*relx + rely*rely + relz*relz );
  451.             balance = acos( relx/distance) / PI;
  452.             amplitude = 1.0 / (distance*distance + 1.0);  
  453.  
  454.             process_oneshot_audio(soundbuf,amplitude,balance,BUFFERSIZE);
  455.         }
  456.         
  457.         while(ALgetfillable(audioport) < BUFFERSIZE)
  458.             sginap(1);  /* Wait til theres enough room to write the entire */
  459.                         /* copy of our sound buffer to the audio queue.    */
  460.  
  461.         ALwritesamps(audioport,soundbuf,BUFFERSIZE); 
  462.                         /* Output samples to audio port */
  463.     }
  464.     
  465.     while(ALgetfilled(audioport) > 0);
  466.         /* Wait till all the sound has been played before closing the port.  */
  467.  
  468.     audioexit(0);
  469. }
  470.  
  471.  
  472. /* Process one-shot audio for bouncing off wall sound - time critical */
  473. void process_oneshot_audio(float *soundbuf, float amplitude,    
  474.                            float balance, long buflen)
  475. {
  476.     static long wallphase;            /* Index into wall's sound buffer */
  477.     long i;                           /* Counter */
  478.     float rbalance = 1.0 - balance;   /* Speeds up calculations */
  479.  
  480.     amplitude *= 5.0;             /* The wall is usually pretty far away */
  481.                                   /* boost up the volume for collisions  */
  482.  
  483.     if(hit == JUST_HIT)   /* If this is the first time around, the index     */
  484.     {                     /* into the sound buffer needs to be reinitialized */
  485.         hit = BEEN_HIT;   /* ialized, otherwise we may need to pick up       */
  486.         wallphase = 0;    /* processing where we left off, since the entire  */
  487.                           /* duration of sound may be longer than the time   */
  488.     }                     /* slice we need to process.                       */
  489.  
  490.     for(i=0; (i<buflen) && (wallphase < wallframes); wallphase++ ) {
  491.  
  492.         /* Since we know that this will be processed _after_ there is valid */
  493.         /* audio data in the sample buffer, we'll perform the operation of  */
  494.         /* mixing the new audio data with the old audio data as the samples */
  495.         /* are copied. In this case the mixing operation is a simple add. A */
  496.         /* weighted average may be needed if the sum is likely to clip.     */
  497.  
  498.         soundbuf[i++] += amplitude * balance * wallbuf[wallphase]; /* Left  */
  499.         soundbuf[i++] += amplitude * rbalance* wallbuf[wallphase]; /* Right */
  500.  
  501.             /* Note that a stereo soundbuffer is interleaved LRLRLR... */
  502.     }
  503.  
  504.     if(wallphase == wallframes)      /* If the sound buffer is exhausted, */
  505.         hit = NO_HIT;                /* we don't need to come back.       */
  506. }
  507.  
  508.  
  509. /* Process audio sample loop for moving ball - time critical */
  510. void process_continuous_audio(float *soundbuf, float amplitude, 
  511.                               float balance, long buflen)
  512. {
  513.     static long ballphase = 0;        /* Index into wall's sound buffer */
  514.     long i;                           /* Counter */
  515.     float rbalance = 1.0 - balance;   /* Speeds up calculations */
  516.  
  517.     for(i=0; i<buflen; ballphase %= ballframes) {
  518.  
  519.         /* Since we know that this will be processed _before_ there is valid */
  520.         /* audio data in the sample buffer, we'll can just copy the data into*/
  521.         /* the buffer, if data were already in the sample buffer, mixing     */
  522.         /* would be necessary.   Continuous looping is acheived by the modulo*/
  523.         /* function above. */
  524.  
  525.         soundbuf[i++] = amplitude * balance * ballbuf[ballphase];   /* Left  */
  526.         soundbuf[i++] = amplitude * rbalance* ballbuf[ballphase++]; /* Right */
  527.  
  528.             /* Note that a stereo soundbuffer is interleaved LRLRLR... */
  529.     }
  530. }
  531.  
  532.  
  533. void audioexit(int exitval)        /* Gracefully exit the audio process */
  534. {
  535.     if (wallbuf != NULL)            free(wallbuf);
  536.     if (ballbuf != NULL)            free(ballbuf);
  537.     if (audioconfig != NULL)        ALfreeconfig(audioconfig);
  538.     if (audioport != NULL)          ALcloseport(audioport);
  539.     if (affh != AF_NULL_FILEHANDLE) AFclosefile(affh);
  540.     if (afsetup != NULL)            AFfreefilesetup(afsetup);
  541.  
  542.     printf("audio closed down OK.\n");
  543.     audiodone = TRUE;
  544.     exit(exitval);
  545. }
  546.  
  547.  
  548. void main()
  549. {
  550.     short val;              /* Value returned from GL device */
  551.     int audio_pid;          /* Process id of child audio process */
  552.     int moveball = TRUE;    /* Flag for ball motion - controlled by LEFTMOUSE */
  553.     long xorigin, yorigin;  /* Origin of GL window */
  554.     long xsize, ysize;      /* Size of GL window */
  555.  
  556.     /* Initialize globals */
  557.     done = audiodone = FALSE;
  558.     rx = ry = 0.0;
  559.     sphx = 1.0; sphy = 2.0; sphz = 3.0;
  560.     movx = 0.1; movy = 0.2;  movz = 0.1;
  561.     hit = NO_HIT;
  562.  
  563.     foreground();
  564.     audio_pid = sproc(audioloop,PR_SALL);     /* Fork audio process */
  565.     if (audio_pid == -1) {
  566.         fprintf(stderr,"Couldn't create audio process.\n");
  567.         exit(-1);
  568.     }
  569.  
  570.     winopen("sonic");                         /* Initialize window */
  571.     RGBmode();
  572.     doublebuffer();
  573.     gconfig();
  574.  
  575.     getorigin(&xorigin, &yorigin);            /* Get window info */
  576.     getsize(&xsize, &ysize);
  577.  
  578.     qdevice(ESCKEY);                          /* Queue devices */
  579.     qdevice(WINQUIT);
  580.     qdevice(REDRAW);
  581.     qdevice(MOUSEX);
  582.     qdevice(MOUSEY);
  583.     qdevice(LEFTMOUSE);
  584.     sceneinit(xsize,ysize);
  585.  
  586.     while (!done && !audiodone) {             /* Event loop */
  587.         while(qtest()) {
  588.             switch(qread(&val)) {
  589.  
  590.                 case WINQUIT:
  591.                     done = TRUE;
  592.                     break;
  593.  
  594.                 case ESCKEY:
  595.                     done = !val;              /* Quit on ESC key up */
  596.                     break;
  597.  
  598.                 case REDRAW:
  599.                     getorigin(&xorigin, &yorigin);
  600.                     getsize(&xsize, &ysize);
  601.                     reshapeviewport();
  602.                     break;
  603.  
  604.                 case MOUSEX:
  605.                     ry=300*(2.0*(val-xorigin)/xsize-1.0);
  606.                     break;
  607.  
  608.                 case MOUSEY:
  609.                     rx=300*(2.0*(val-yorigin)/ysize-1.0);
  610.                     break;
  611.  
  612.                 case LEFTMOUSE:               /* Stop/start ball motion */
  613.                     if(val) {
  614.                         moveball = !moveball;
  615.                         hit = JUST_HIT;
  616.                         hitx = sphx; hity=sphy; hitz=sphz;
  617.                     }
  618.                     break;
  619.             }
  620.         }
  621.         if (moveball) movesphere();
  622.         drawscene();
  623.     }
  624.     fprintf(stderr,"out of gfx event loop.\n");
  625.     while (!audiodone)
  626.     sginap(1);        /* Wait for audio to shut down properly */
  627.  
  628.     delobj(box);
  629.     delobj(sphere);
  630.     exit(0);
  631. }
  632.